package levelrewind.android.app.model;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import levelrewind.android.app.vo.Person;
import levelrewind.android.app.vo.User;
import levelrewind.android.app.vo.UserScore;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.facebook.android.AsyncFacebookRunner;
import com.facebook.android.AsyncFacebookRunner.RequestListener;
import com.facebook.android.DialogError;
import com.facebook.android.Facebook;
import com.facebook.android.Facebook.DialogListener;
import com.facebook.android.FacebookError;
import com.facebook.android.Util;

/**
 * This class is for data communication with Facebook.
 * In this application uses the facebook-user to login to the system.
 * */
@SuppressWarnings("unused")
public class FacebookHandler  {
	private static final String FACEBOOK_APPID = "500727406604106";
	public static Facebook facebook = new Facebook(FACEBOOK_APPID);
	private AsyncFacebookRunner mAsyncRunner;
	private SharedPreferences mPrefs;
	
	private Context context;
	private IBaseActivity base;
	private ArrayList<User> friends;
	private Person curPerson;
	
	/*
	 * Contains data of logged in user.
	 * */
	public static User loggedInUser;
	
	/*
	 * Permission to get another data than the generale data
	 * email: asks facebook to allow to use email on application
	 * public_stream: asks facebook to post on the wall
	 * */
	private String[] PERMISSIONS = {"email", "publish_stream" };
	
	/*
	 * isLoggedIn : true, if user is logged in with facebook, otherwise false
	 * */
	public static boolean isLoggedIn;
	private boolean isUpdateScore;
	
	public FacebookHandler(Context context) {
		this.context = context;
		base = (IBaseActivity) context;
		mAsyncRunner = new AsyncFacebookRunner(facebook);
		isLoggedIn = false;
		isUpdateScore = false;
	}

	/**
	 * Login to the system with facebook user.
	 * If user is has logged in once and access_token is still valid, go direct to request data function.
	 * Else authorize user and set access_token before requesting data.
	 * */
	public void login() {
   	 	/*
        * Get existing access_token if any
        */
		mPrefs = context.getSharedPreferences("userLogin", Context.MODE_PRIVATE);
		String access_token = mPrefs.getString("access_token", null);
		long expires = mPrefs.getLong("access_expires", 0);
		if(access_token != null) {
			facebook.setAccessToken(access_token);
		}
		if(expires != 0) {
		   facebook.setAccessExpires(expires);
		}
       
        if (!facebook.isSessionValid()) {
       	   AlertDialog.Builder builder = new AlertDialog.Builder(context);
           builder.setMessage("Before you can submit your scores to this app you need to log in using your Facebook account.\nLog in now ?")
                  .setCancelable(false)
                  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                      public void onClick(DialogInterface dialog, int id) {

                   	   facebook.authorize((Activity) context, PERMISSIONS, Facebook.FORCE_DIALOG_AUTH, new DialogListener() {
                              @Override
                              public void onComplete(Bundle values) {    
	                       		   mAsyncRunner.request("me", new meRequestListener());
	                       		   Log.i("FACEBOOK", "You are logged in.");
                              }

                              @Override
                              public void onFacebookError(FacebookError error) {}

                              @Override
                              public void onError(DialogError e) {}

                              @Override
                              public void onCancel() {}
                          });
                      }
                  })
                  .setNegativeButton("No", new DialogInterface.OnClickListener() {
                      public void onClick(DialogInterface dialog, int id) {
                           dialog.cancel();
                           Toast.makeText(context, "You can still play the games connected to this app, but you will not be able to post results and support your idol.", Toast.LENGTH_LONG).show();
                      }
                  });
           AlertDialog alert = builder.create();
           alert.show();
		}
        else { 
	        try { 
	        	Log.i("Facebook Login", "Has valid session");
    		    mAsyncRunner.request("me", new meRequestListener());
	        } 
	        catch(Exception ex ) {
	        	ex.printStackTrace();
	        	Log.e("FACEBOOK", ex.getMessage());
	    	}
       }
   }
	
	/**
	 * Logout from the system.
	 * Remove access_token from sharedPreferences, 
	 * and update status on the screen to inform that user is not on login.
	 * */
	public void logout() {
    	facebook.setAccessToken(null);
    	facebook.setAccessExpires(0);
    	facebook.setAccessExpiresIn(null);
    	mAsyncRunner.logout(context, new RequestListener() {
			
			@Override
			public void onMalformedURLException(MalformedURLException e, Object state) {
			}
			
			@Override
			public void onIOException(IOException e, Object state) {
			}
			
			@Override
			public void onFileNotFoundException(FileNotFoundException e, Object state) {
			}
			
			@Override
			public void onFacebookError(FacebookError e, Object state) {
			}
			
			@Override
			public void onComplete(String response, Object state) {
				isLoggedIn = false;
				loggedInUser = null;
//				base.displayUserLoggedInStatus();
				SharedPreferences.Editor editor = mPrefs.edit();
				editor.clear();
				editor.commit();
	        	Log.i("FACEBOOK", "You are logout.");
			}
		});
	}
	
	
	/**
	 * Getting facebook friends who have installed the application.
	 * @param curPerson
	 */
	public void getFriends(Person curPerson) {
		 Log.i(this.getClass().getSimpleName(), "Download friends");
		 this.curPerson = curPerson;
		 friends = new ArrayList<User>();
		 String query = "SELECT uid,name FROM user WHERE uid IN(SELECT uid2 FROM friend WHERE uid1 = me()) AND is_app_user = 1"; 
		 Bundle params = new Bundle(); 
		 params.putString("format", "json"); 
		 params.putString("method", "fql.query"); 
		 params.putString("query", query); 
		 params.putString("access_token",  facebook.getAccessToken()); 

		 mAsyncRunner.request(params, new friendsRequestListener());
	}

	
	/**
	 * Converting imageURL to Bitmap.
	 * @param imageURL
	 * @return Bitmap
	 */
	private Bitmap getBitmapPicture(String imageURL) {
		Bitmap picture = null;
		try {
			picture = BitmapFactory.decodeStream((InputStream)new URL(imageURL).getContent());
			//Resize picture to 50x50
			int width = 50, height = 50;
			picture = Bitmap.createScaledBitmap(picture, width, height, true); 
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} 
		
		return picture;
	}
	
	public Facebook getFacebook() {
		return facebook;
	}

	public void setUpdateScore(boolean isUpdateScore) {
		this.isUpdateScore = isUpdateScore;
	}
	
	/**
	 * Getting friends score from database
	 */
	private class GetFriendScoreTask extends AsyncTask<Integer, Void, UserScore[]>
    {
    	protected void onPostExecute(UserScore[] result)
    	{
    		if (result != null) {
	    		Map<Long, Long> map = new HashMap<Long, Long>();
	    		for (UserScore us : result) {	
	    			map.put(us.getUserID(), us.getScore());
	    		}
	    		
	    		ArrayList<User> friendsByPerson = new ArrayList<User>();
	    		for (int i = 0; i < friends.size(); i++) {
	    			long userId = friends.get(i).getId();
	    			if (map.containsKey(userId)) {
	    				friends.get(i).setScore(map.get(userId));
	    				friendsByPerson.add(friends.get(i));
	    			}
	    		}
	    		User[] arr = new User[friendsByPerson.size()];
	    		Arrays.sort(friendsByPerson.toArray(arr));
//	    		base.displayFriendsScore(arr);
    		}
		}
    	
    	
		@Override
		protected UserScore[] doInBackground(Integer... params) {
			return RestDataHandler.getUserScoreByPerson(params[0]);
		}
    	
    }
    
	private class meRequestListener implements RequestListener {

    	@Override
    	public void onComplete(String response, Object state) {
    		JSONObject json;
    		try {
    			json = Util.parseJson(response);
    			String facebookID = json.getString("id"); 
    			String name = json.getString("name");
    			String email = json.getString("email");
    			String imageURL = "http://graph.facebook.com/"+facebookID+"/picture?type=small";
    			Bitmap pic = getBitmapPicture(imageURL);
    			
    			SharedPreferences.Editor editor = mPrefs.edit();
                editor.putString("access_token", facebook.getAccessToken());
                editor.putLong("access_expires", facebook.getAccessExpires());
                editor.commit();
                
    			loggedInUser = new User();
    			loggedInUser.setId(Long.parseLong(facebookID));
    			loggedInUser.setName(name);
    			loggedInUser.setEmail(email);
    			loggedInUser.setPicture(pic);
    			
                if (!isLoggedIn) RestDataHandler.CreateUser(loggedInUser);
                isLoggedIn = true;  
                if (isUpdateScore) {
//                	base.updatePersonScoreAndPlace(true);
                	Log.i("FacebookHandler", "Call updatePersonScoreAndPlace");
                	isUpdateScore = false;
                }
//                base.displayUserLoggedInStatus();
                Log.i("Facebook Login", "Login successful:"+name);
    			
    		} catch (FacebookError e) {
    			e.printStackTrace();
    		} catch (JSONException e) {
    			e.printStackTrace();
    		} 
    		

    	}

    	@Override
    	public void onIOException(IOException e, Object state) {

    	}

    	@Override
    	public void onFileNotFoundException(FileNotFoundException e, Object state) {

    	}

    	@Override
    	public void onMalformedURLException(MalformedURLException e, Object state) {

    	}

    	@Override
    	public void onFacebookError(FacebookError e, Object state) {
    	}

    }
	
	private class friendsRequestListener implements RequestListener {

		@Override
		public void onComplete(String response, Object state) {
			try {
				JSONArray jArray = new JSONArray(response);
		        for(int i=0;i<jArray.length();i++){ 

		        		JSONObject json_data = jArray.getJSONObject(i); 
		        		User user = new User();
		        		user.setId(json_data.getLong("uid"));
		        		user.setName(json_data.getString("name"));
		        		String imageURL = "http://graph.facebook.com/"+user.getId()+"/picture?type=small"; 
		        		Bitmap picture = getBitmapPicture(imageURL); 
		        		user.setPicture(picture);
		        		friends.add(user);
		         }
		        //Add myself
		        friends.add(loggedInUser);
		        
		        new GetFriendScoreTask().execute(curPerson.getId());
		        
			} catch (FacebookError e) {
				e.printStackTrace();
			} catch (JSONException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void onIOException(IOException e, Object state) {

		}

		@Override
		public void onFileNotFoundException(FileNotFoundException e, Object state) {

		}

		@Override
		public void onMalformedURLException(MalformedURLException e, Object state) {

		}

		@Override
		public void onFacebookError(FacebookError e, Object state) {

		}
	}
   
	public class UpdateStatusListener extends BaseDialogListener {
        @Override
        public void onComplete(Bundle values) {
            final String postId = values.getString("post_id");
            if (postId != null) {
            	// Wall post successful
            } else {
            	// No wall post made
            }
        }

        @Override
        public void onFacebookError(FacebookError error) {
            Toast.makeText(context, "Facebook Error: " + error.getMessage(),
                    Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onCancel() {
            Toast toast = Toast.makeText(context, "Update status cancelled",
                    Toast.LENGTH_SHORT);
            toast.show();
        }
    }
}
